package com.jeff.android.lab.bugs.toast.repair;

import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.widget.Toast;

import androidx.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;

/**
 * @author: dingfeng
 * @date: 2020-04-14
 * @since: 1.0.0
 **/
public class ToastUtil {
    private static Toast mToast;
    private static WeakReference<Handler> mHandlerReference;

    public static void show(Context context, int resId) {
        show(context, context.getString(resId), Toast.LENGTH_SHORT);
    }

    public static void show(final Context context, final CharSequence text) {
        show(context, text, Toast.LENGTH_LONG);
    }

    public static void show(Context context, int resId, @Duration int duration) {
        show(context, context.getString(resId), duration);
    }

    public static void show(final Context context, final CharSequence text, @Duration final int duration) {
        if (context == null || TextUtils.isEmpty(text)) {
            return;
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
            if (null == mHandlerReference) {
                mHandlerReference = new WeakReference<>(new Handler(Looper.getMainLooper()));
            }
            Handler handler = mHandlerReference.get();
            if (handler != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        show(context, text, duration);
                    }
                });
            }
        } else {
            showToast(context, text, duration);
        }
    }

    private static void showToast(Context context, CharSequence text, @Duration int duration) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Toast.makeText(context, text, duration).show();
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
            ToastCompat.showToast(context, text, duration);
        } else {
            if (mToast != null) {
                mToast.setText(text);
            } else {
                mToast = Toast.makeText(context, text, duration);
            }
            mToast.show();
        }
    }

    @IntDef(value = {
            Toast.LENGTH_SHORT,
            Toast.LENGTH_LONG
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Duration {
    }


    /*
     *
     * android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@5bb4737 is not valid; is your activity running?
     *
     * Android 7.1.1 7.1.2 BadTokenException 兼容
     * */
    public static class ToastCompat {

        private static Field sField_TN;
        private static Field sField_TN_Handler;

        static {
            try {
                sField_TN = Toast.class.getDeclaredField("mTN");
                sField_TN.setAccessible(true);
                sField_TN_Handler = sField_TN.getType().getDeclaredField("mHandler");
                sField_TN_Handler.setAccessible(true);
            } catch (Exception e) {
            }
        }

        private static void hook(Toast toast) {
            try {
                Object tn = sField_TN.get(toast);
                Handler preHandler = (Handler) sField_TN_Handler.get(tn);
                sField_TN_Handler.set(tn, new SafelyHandlerWrapper(preHandler));
            } catch (Exception e) {
            }
        }

        public static void showToast(Context context, CharSequence cs, int length) {
            Toast toast = Toast.makeText(context, cs, length);
            hook(toast);
            toast.show();
        }

    }

    private static class SafelyHandlerWrapper extends Handler {

        private Handler impl;

        public SafelyHandlerWrapper(Handler impl) {
            this.impl = impl;
        }

        @Override
        public void dispatchMessage(Message msg) {
            try {
                super.dispatchMessage(msg);
            } catch (Exception e) {
            }
        }

        @Override
        public void handleMessage(Message msg) {
            impl.handleMessage(msg);//需要委托给原Handler执行
        }
    }

}
